﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace LyricTrackerPlugin
{
    public class Lyric : IComparable
    {
        public TimeSpan Time { get; set; }
        public String Text { get; set; }

        #region IComparable Members
        public int CompareTo(object obj)
        {
            var lyric = obj as Lyric;
            if (lyric != null)
            {
                return this.Time.CompareTo(lyric.Time);
            }
            return 0;
        }
        #endregion

        public int Dif { get; set; }
        public Lyric Next { get; set; }
        public int ScrollOffset { get; set; }
        public int Offset { get; set; }
    }

    public class LyricCollection : List<Lyric>, IDisposable
    {
        public String By { get; private set; }
        public String Artist { get; private set; }
        public String Title { get; private set; }
        public String Album { get; private set; }
        public int Offset { get; private set; }

        private const String _titlePattern = @"\[ti:(?<info>[^\]]*)\]";
        private const String _artistPattern = @"\[ar:(?<info>[^\]]*)\]";
        private const String _byPattern = @"\[by:(?<info>[^\]]*)\]";
        private const String _albumPattern = @"\[al:(?<info>[^\]]*)\]";
        private const String _offsetPattern = @"\[offset:(?<info>\d+)\]";
        private const String _lyricPattern = @"(?:\[(?<t>\d{2}:\d{2}.\d{2})\])+(?<l>[^\r\n]*)";
        private const String _timePattern = @"(?<m>\d{2}):(?<s>\d{2}).(?<ms>\d{2})";
        private String _rawLyric;
        public String RawLyric
        {
            get { return _rawLyric; }
        }

        public LyricCollection(String rawLyric)
        {
            _rawLyric = rawLyric;
            Init(rawLyric);
        }

        private void Init(String rawLyric)
        {
            var match = Regex.Match(rawLyric, _titlePattern);
            if (match.Success)
            {
                Title = match.Groups["info"].Value;
            }
            match = Regex.Match(rawLyric, _artistPattern);
            if (match.Success)
            {
                Artist = match.Groups["info"].Value;
            }
            match = Regex.Match(rawLyric, _byPattern);
            if (match.Success)
            {
                By = match.Groups["info"].Value;
            }
            match = Regex.Match(rawLyric, _albumPattern);
            if (match.Success)
            {
                Album = match.Groups["info"].Value;
            }
            match = Regex.Match(rawLyric, _offsetPattern);
            if (match.Success)
            {
                Offset = Convert.ToInt32(match.Groups["info"].Value);
            } 
            var matches = Regex.Matches(rawLyric, _lyricPattern);
            foreach (Match matche in matches)
            {
                if (matche.Success)
                {
                    var text = matche.Groups["l"].Value;
                    foreach (var item in matche.Groups["t"].Captures)
                    {
                        var lyric = new Lyric();
                        lyric.Text = text;
                        var m = Regex.Match(item.ToString(), _timePattern);
                        if (m.Success)
                        {
                            lyric.Time = new TimeSpan(0, 0, Convert.ToInt16(m.Groups["m"].Value), Convert.ToInt16(m.Groups["s"].Value), Convert.ToInt16(m.Groups["ms"].Value) * 10);
                        }
                        this.Add(lyric);
                    }
                    
                }
            }
            this.Sort();
            for (int i = 0; i < this.Count - 1; i++)
            {
                this[i].Next = this[i + 1];
                this[i].Next.Offset = this[i].Offset + this[i].Text.Length + 1;
                if (i >= 3)
                {
                    this[i].Next.ScrollOffset = this[i - 3].Offset;
                }
            }
            if (this.Count > 0)
            {
                this[this.Count - 1].Dif = 1000 * 1000;
            }
            foreach (var lyric in this)
            {
                _lyrics.Add(lyric.Text);
            }
        }

        private readonly List<String> _lyrics = new List<String>();

        public override String ToString()
        {
            return String.Join(Environment.NewLine, _lyrics.ToArray());
        }

        private Lyric _currentLyric;
        public Lyric Seek(TimeSpan timeSpan)
        {
            var justedTimeSpan = timeSpan.Add(_justTimeSpan);
            foreach (var lyric in this)
            {
                if (lyric.Time.CompareTo(justedTimeSpan) > 0)
                {
                    if (_currentLyric == null)
                    {
                        _currentLyric = lyric;
                        if (lyric.Next != null)
                        {
                            lyric.Dif = (int)lyric.Next.Time.Subtract(justedTimeSpan).TotalMilliseconds;
                        }
                    }
                    break;
                }
                _currentLyric = lyric;
                if (lyric.Next != null)
                {
                    lyric.Dif = (int)lyric.Next.Time.Subtract(justedTimeSpan).TotalMilliseconds;
                }
            }
            return _currentLyric;
        }

        public Lyric Next(TimeSpan timeSpan)
        {
            var justedTimeSpan = timeSpan.Add(_justTimeSpan);
            if (_currentLyric != null)
            {
                if (_currentLyric.Next != null)
                {
                    _currentLyric.Dif = (int)_currentLyric.Next.Time.Subtract(justedTimeSpan).TotalMilliseconds;
                }
                if (_currentLyric.Dif <= 0)
                {
                    _currentLyric = _currentLyric.Next;
                    Next(timeSpan);
                }
            }
            return _currentLyric;
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.Clear();
            this._rawLyric = null;
        }

        #endregion

        private TimeSpan _justTimeSpan;
        public void Just(TimeSpan timeSpan)
        {
            _justTimeSpan = _justTimeSpan.Add(timeSpan);
        }
    }
}
